using System;
using System.Collections;
using System.Collections.Generic;
using Me.Prettyprint.Cassandra.Model;
using Me.Prettyprint.Cassandra.Service;
using Me.Prettyprint.Hector.Api;
using Me.Prettyprint.Hector.Api.Exceptions;

namespace Me.Prettyprint.Cassandra.Service
{
    /// <summary>
    /// Operation is the base class for all operations sent to Cassandra nodes. HConnectionManager
    /// uses the Operation classes to do the real work and handles the retry logic.
    /// </summary>
    /// <typeparam name="T"></typeparam>
	public abstract class Operation<T>
	{
        public Operation(OperationType operationType, Dictionary<string,string> credentials) : this(operationType,FailoverPolicy.ON_FAIL_TRY_ALL_AVAILABLE,null,credentials)
        {
        }
        public Operation(OperationType operationType, FailoverPolicy failoverPolicy, Dictionary<string,string> credentials) : this(operationType, failoverPolicy, null, credentials)
        {
        }
        public Operation(OperationType operationType)
        {
            FailCounter = (operationType == OperationType.READ) ? CassandraClientMonitor.Counter.READ_FAIL : CassandraClientMonitor.Counter.WRITE_FAIL;
            OperationType = operationType;
            StopWatchTagName = Enum.GetName(typeof(OperationType), operationType);
        }
        public Operation(OperationType operationType, FailoverPolicy failoverPolicy, String keyspaceName, Dictionary<string, string> credentials)
        {
            FailCounter = (operationType == OperationType.READ) ? CassandraClientMonitor.Counter.READ_FAIL : CassandraClientMonitor.Counter.WRITE_FAIL;
            OperationType = operationType;
            StopWatchTagName = Enum.GetName(typeof(OperationType), operationType);
            FailoverPolicy = failoverPolicy;
            KeyspaceName = keyspaceName;
            Credentials = new Dictionary<string,string>(credentials);
        }
        public void ApplyConnectionParams(String keyspace, ConsistencyLevelPolicy consistencyLevelPolicy,
            FailoverPolicy failoverPolicy, Dictionary<string, string> credentials)
        {
            KeyspaceName = keyspace;
            ConsistencyLevelPolicy = consistencyLevelPolicy;
            FailoverPolicy = failoverPolicy;
            Credentials = new Dictionary<string, string>(credentials);
        }
        public T Result
		{
            get;
            set;
		}
		public HectorException Exception
		{
			get;
			set;
		}
        public bool HasException
        {
            get
            {
                return Exception != null;
            }
        }
		public CassandraHost CassandraHost
		{
			get;
            protected set;
		}
		public ConsistencyLevelPolicy ConsistencyLevelPolicy
        {
            get;
            protected set;
        }
        public FailoverPolicy FailoverPolicy
        {
            get;
            set;
        }
        public string StopWatchTagName
        {
            get;
            protected set;
        }
		public CassandraClientMonitor.Counter FailCounter
        {
            get;
            private set;
        }
		public string KeyspaceName
        {
            get;
            set;
        }
		public Dictionary<string,string> Credentials
        {
            get;
            set;
        }
		protected internal long ExecTime
        {
            get;
            private set;
        }
		public OperationType OperationType
        {
            get;
            set;
        }
        public ExecutionResult<T> getExecutionResult()
        {
            return new ExecutionResult<T>(Result, ExecTime, CassandraHost);
        }
        public abstract T Execute(Org.Apache.Cassandra.Thrift.Cassandra.Client cassandra);
        public void ExecuteAndSetResult(Org.Apache.Cassandra.Thrift.Cassandra.Client cassandra, CassandraHost cassandraHost)
        {
            CassandraHost = cassandraHost;
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            Result = Execute(cassandra);
            watch.Stop();
            ExecTime = watch.ElapsedTicks;
        }
	}
    /// <summary>
    /// We use this with Lambda expression since Java Hector uses anonymous
    /// inner classes that extend the abstract Operation class. In C# we
    /// can't create instances of abstract classes like java. This allows
    /// us to pass the function to it and have Execute method use the
    /// function to do the actual work.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class OperationBuilder<T> : Operation<T>
    {
        Func<Org.Apache.Cassandra.Thrift.Cassandra.Client, T> act
        {
            get;
            set;
        }
        /// <summary>
        /// The default OperationType is READ
        /// </summary>
        /// <param name="Action"></param>
        public OperationBuilder(Func<Org.Apache.Cassandra.Thrift.Cassandra.Client, T> Action) : base(OperationType.READ)
        {
            act = Action;
            FailoverPolicy = FailoverPolicy.FAIL_FAST;
        }
        public override T Execute(Org.Apache.Cassandra.Thrift.Cassandra.Client cassandra)
        {
            return act(cassandra);
        }
    }
}